package com.vanke.kotlin_mvp.data

import com.vanke.kotlin_mvp.data.local.TasksLocalDataSource
import com.vanke.kotlin_mvp.data.remote.TasksRemoteDataSource

/**
 * @name Kotlin_MVP
 * @package name：com.vanke.kotlin_mvp.data
 * @class describe
 * @anthor shiyuchuan
 * @time 2017/8/21 9:58
 */
class TaskRepository(val tasksRemoteDataSource: TasksRemoteDataSource,
                     val tasksLocalDataSource: TasksLocalDataSource) : TaskDataSource {

    var cachedTasks: LinkedHashMap<String, Task> = LinkedHashMap()

    var cacheIsDirty = false

    override fun getTasks(callback: TaskDataSource.LoadTaskCallback) {
        if (cachedTasks.isNotEmpty() && !cacheIsDirty) {
            callback.onTaskLoaded(ArrayList(cachedTasks.values))
            return
        }

        if (cacheIsDirty) {
            getTasksFromRemoteDataSource(callback)
        } else {
            tasksLocalDataSource.getTasks(object : TaskDataSource.LoadTaskCallback {
                override fun onTaskLoaded(tasks: List<Task>) {
                    refreshCache(tasks)
                    callback.onTaskLoaded(ArrayList(cachedTasks.values))
                }

                override fun onDataNotAvailable() {
                    getTasksFromRemoteDataSource(callback)
                }

            })
        }
    }

    private fun refreshCache(tasks: List<Task>) {
        cachedTasks.clear()
        for (task in tasks)
            cacheAndPerform(task, {})
        cacheIsDirty = false
    }

    private fun getTasksFromRemoteDataSource(callback: TaskDataSource.LoadTaskCallback) {
        tasksRemoteDataSource.getTasks(object : TaskDataSource.LoadTaskCallback {
            override fun onTaskLoaded(tasks: List<Task>) {
                refreshCache(tasks)
                refreshLocalDataSource(tasks)
                callback.onTaskLoaded(ArrayList(cachedTasks.values))
            }

            override fun onDataNotAvailable() {
                callback.onDataNotAvailable()
            }

        })
    }

    private fun refreshLocalDataSource(tasks: List<Task>) {
        tasksLocalDataSource.deleteAllTasks()
        for (task in tasks)
            tasksLocalDataSource.saveTask(task)
    }

    override fun getTask(taskId: String, callback: TaskDataSource.GetTaskCallback) {
        val taskInCache = getTaskWithId(taskId)

        if (taskInCache != null) {
            callback.onTaskLoaded(taskInCache)
            return
        }

        tasksLocalDataSource.getTask(taskId, object : TaskDataSource.GetTaskCallback {
            override fun onTaskLoaded(task: Task) {
                cacheAndPerform(task, { cacheTask ->
                    callback.onTaskLoaded(cacheTask)
                })
            }

            override fun onDataAvailable() {
                tasksRemoteDataSource.getTask(taskId, object : TaskDataSource.GetTaskCallback {
                    override fun onTaskLoaded(task: Task) {
                        cacheAndPerform(task, { cachetask ->
                            callback.onTaskLoaded(cachetask)
                        })
                    }

                    override fun onDataAvailable() {
                        callback.onDataAvailable()
                    }

                })
            }

        })
    }

    override fun saveTask(task: Task) {
        cacheAndPerform(task, { cachedTask ->
            tasksRemoteDataSource.saveTask(cachedTask)
            tasksLocalDataSource.saveTask(cachedTask)
        })
    }

    private inline fun cacheAndPerform(task: Task, perform: (Task) -> Unit) {
        val cacheTask = Task(task.title, task.description, task.id).apply {
            isCompleted = task.isCompleted
        }
        cachedTasks.put(cacheTask.id, cacheTask)
        perform(cacheTask)
    }

    override fun completeTask(task: Task) {
        cacheAndPerform(task, { cachedTask ->
            cachedTask.isCompleted = true
            tasksRemoteDataSource.completeTask(cachedTask)
            tasksLocalDataSource.completeTask(cachedTask)
        })
    }

    override fun completeTask(taskId: String) {
        getTaskWithId(taskId)?.let {
            completeTask(it)
        }
    }

    private fun getTaskWithId(taskId: String): Task? {
        return if (cachedTasks.isEmpty()) null else cachedTasks[taskId]
    }

    override fun activateTask(task: Task) {
        cacheAndPerform(task, { cacheTask ->
            cacheTask.isCompleted = false
            tasksRemoteDataSource.activateTask(cacheTask)
            tasksLocalDataSource.activateTask(cacheTask)
        })
    }

    override fun activateTask(taskId: String) {
        getTaskWithId(taskId)?.let {
            activateTask(it)
        }
    }

    override fun clearCompletedTasks() {
        tasksRemoteDataSource.clearCompletedTasks()
        tasksLocalDataSource.clearCompletedTasks()

        with(cachedTasks.entries.iterator()) {
            while (hasNext()) {
                val entry = next()
                if (entry.value.isCompleted == true)
                    remove()
            }
        }
    }

    override fun refreshTasks() {
        cacheIsDirty = true
    }

    override fun deleteAllTasks() {
        tasksRemoteDataSource.deleteAllTasks()
        tasksLocalDataSource.deleteAllTasks()
        cachedTasks.clear()
    }

    override fun deleteTask(taskId: String) {
        tasksRemoteDataSource.deleteTask(taskId)
        tasksLocalDataSource.deleteTask(taskId)
        cachedTasks.remove(taskId)
    }

    companion object {
        private lateinit var INSTANCE: TaskRepository
        private var needNewInstance = true

        fun getInstance(tasksRemoteDataSource: TasksRemoteDataSource, tasksLocalDataSource: TasksLocalDataSource): TaskRepository {
            if (needNewInstance) {
                INSTANCE = TaskRepository(tasksRemoteDataSource, tasksLocalDataSource)
                needNewInstance = false
            }
            return INSTANCE
        }

        fun destroyInstance() {
            needNewInstance = true
        }
    }
}